#pragma once
#include "logger.hpp"
#include "util.hpp"
#include <mutex>
#include <cassert>

namespace liang
{
    class userTable
    {
    public:
        userTable(const std::string &host,
                   const std::string &user,
                   const std::string &password,
                   const std::string &dbname,
                   uint16_t port = 3306)
        {
            _mysql = MysqlHelper::mysql_create(host, user, password, dbname, port);
            assert(_mysql != nullptr);
        }
        ~userTable()
        {
            MysqlHelper::mysql_destory(_mysql);
        }

        // 注册用户
        bool registration(Json::Value &user)
        {
#define INSERT_USER "insert into user values(null, '%s', SHA2('%s', 256), 1000, 0, 0);"
            if (user["username"].isNull() || user["password"].isNull())
            {
                DEBUG("input username or password");
                return false;
            }
            // 插入用户信息
            char sql[4096] = {0};
            sprintf(sql, INSERT_USER, user["username"].asCString(), user["password"].asCString());
            if (!MysqlHelper::mysql_exec(_mysql, sql))
            {
                DEBUG("insert user[%s] error", user["username"].asCString());
                return false;
            }
            return true;
        }
        // 登录
        bool login(Json::Value &user)
        {
#define LOGIN_USER "select id, score, total_count, win_count from user where username='%s' and password=SHA2('%s', 256);"
            // 查询信息
            char sql[4096] = {0};
            sprintf(sql, LOGIN_USER, user["username"].asCString(), user["password"].asCString());

            MYSQL_RES *res = nullptr;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if (!MysqlHelper::mysql_exec(_mysql, sql))
                {
                    DEBUG("user[%s] login error", user["username"].asCString());
                    return false;
                }
                // 保存信息
                res = mysql_store_result(_mysql);
                if (res == nullptr)
                {
                    DEBUG("have no login user[%s] information", user["username"].asCString());
                    return false;
                }
            }

            size_t rows = mysql_num_rows(res);
            if (rows != 1)
            {
                DEBUG("have no login user[%s] information or password is wrong", user["username"].asCString());
                return false;
            }
            MYSQL_ROW row = mysql_fetch_row(res);
            user["id"] = (Json::UInt64)std::stol(row[0]);
            user["score"] = (Json::UInt64)std::stol(row[1]);
            user["total_count"] = (Json::UInt64)std::stol(row[2]);
            user["win_count"] = (Json::UInt64)std::stol(row[3]);
            mysql_free_result(res);
            return true;
        }
        // 查询(通过用户名)
        bool selectByName(const std::string &name, Json::Value &user)
        {
#define SELECT_USER_BY_NAME "select id, score, total_count, win_count from user where username='%s';"
            // 查询信息
            char sql[4096] = {0};
            sprintf(sql, SELECT_USER_BY_NAME, name.c_str());

            MYSQL_RES *res = nullptr;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if (!MysqlHelper::mysql_exec(_mysql, sql))
                {
                    DEBUG("get user[%s] by name error", name.c_str());
                    return false;
                }
                // 保存信息
                res = mysql_store_result(_mysql);
                if (res == nullptr)
                {
                    DEBUG("have no user[%s] information", name.c_str());
                    return false;
                }
            }

            size_t rows = mysql_num_rows(res);
            if (rows != 1)
            {
                DEBUG("the user[%s] information queried is not unique", name.c_str());
                return false;
            }
            MYSQL_ROW row = mysql_fetch_row(res);
            user["id"] = (Json::UInt64)std::stol(row[0]);
            user["username"] = name;
            user["score"] = (Json::UInt64)std::stol(row[1]);
            user["total_count"] = (Json::UInt64)std::stol(row[2]);
            user["win_count"] = (Json::UInt64)std::stol(row[3]);
            mysql_free_result(res);
            return true;
        }
        // 查询(通过ID)
        bool selectById(uint64_t id, Json::Value &user)
        {
#define SELECT_USER_BY_ID "select username, score, total_count, win_count from user where id=%ld;"
            // 查询信息
            char sql[4096] = {0};
            sprintf(sql, SELECT_USER_BY_ID, id);

            MYSQL_RES *res = nullptr;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if (!MysqlHelper::mysql_exec(_mysql, sql))
                {
                    DEBUG("get user by id[id:%ld] error", id);
                    return false;
                }
                // 保存信息
                res = mysql_store_result(_mysql);
                if (res == nullptr)
                {
                    DEBUG("have no user[id:%ld] information", id);
                    return false;
                }
            }
            size_t rows = mysql_num_rows(res);
            if (rows != 1)
            {
                DEBUG("the user[id:%ld] information queried is not unique", id);
                return false;
            }
            MYSQL_ROW row = mysql_fetch_row(res);
            user["id"] = (Json::UInt64)id;
            user["username"] = row[0];
            user["score"] = (Json::UInt64)std::stol(row[1]);
            user["total_count"] = (Json::UInt64)std::stol(row[2]);
            user["win_count"] = (Json::UInt64)std::stol(row[3]);
            mysql_free_result(res);
            return true;
        }
        // 胜利一场:分数加30
        bool win(uint64_t id)
        {
#define USER_WIN "update user set score=score+30, total_count=total_count+1, win_count=win_count+1 where id=%ld;"
            char sql[4096] = {0};
            sprintf(sql, USER_WIN, id);
            if (!MysqlHelper::mysql_exec(_mysql, sql))
            {
                DEBUG("update win user[id:%ld] info error", id);
                return false;
            }
            return true;
        }
        // 失败一场:分数减30
        bool lose(uint64_t id)
        {
#define USER_LOSE "update user set score=score-30, total_count=total_count+1 where id=%ld;"
            char sql[4096] = {0};
            sprintf(sql, USER_LOSE, id);
            if (!MysqlHelper::mysql_exec(_mysql, sql))
            {
                DEBUG("update lose user[id:%ld] info error", id);
                return false;
            }
            return true;
        }

    private:
        MYSQL *_mysql;
        std::mutex _mutex;
    };
}